﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;

//------------------------------------------------------------
// shaco Framework
// Copyright © 2017-2021 chang.liu All rights reserved.
// Feedback: 449612236@qq.com
//------------------------------------------------------------

namespace shacoEditor
{
    [System.Diagnostics.DebuggerStepThrough]
    public class GameHelperWindow : EditorWindow
    {
        public class InterfaceInfo
        {
            public object interfaceTarget = null;
            public System.Reflection.PropertyInfo propertyInfo = null;
            public System.Action<InterfaceInfo> drawInterfaceCallBack = null;

            public string header = string.Empty;
            public System.Type[] ignoreTypes = null;
            public bool isCustomDrawerOrBaseType = false;
            public System.Type defaultListNewValueType = null;
            public System.Type genericListValueType = null;
        }

        private shaco.Base.IDataSave _configData = null;
        private shaco.Instance.Editor.TreeView.WindowSplitter _dragLineSeparator = new shaco.Instance.Editor.TreeView.WindowSplitter();
        [SerializeField]
        private string _searchName = string.Empty;
        [SerializeField]
        private string _searchNameLower = string.Empty;
        private Vector2 _leftWindowScrollPos = Vector2.zero;
        // private Vector2 _rightWindowScrollPos = Vector2.zero;
        private readonly GUIContent GUI_CONTENT_EXCEL_LOAD_FILE_TYPE = new GUIContent("Excel Load File Type", "excel配置表运行时刻文件加载类型");
        private readonly GUIContent GUI_CONTENT_GAME_HELPER_SETTING = new GUIContent("Game Helper Setting", "游戏管理器配置，设定管理器下所有接口的实现类");

        private Dictionary<string, List<InterfaceInfo>> _interfacesInfoList = new Dictionary<string, List<InterfaceInfo>>();
        private Dictionary<string, List<InterfaceInfo>> _interfacesInfoBase = new Dictionary<string, List<InterfaceInfo>>();
        private shaco.Base.IGameHelper _currentGameHelper = null;
        private bool _isShacoFrameworkGameHelper = true;

        [MenuItem("shaco/GameHelper %#g", false, (int)ToolsGlobalDefine.MenuPriority.Default.GAMEH_HELPER)]
        static void Open()
        {
            EditorHelper.GetWindow<GameHelperWindow>(null, true, "GameHelper");
        }

        void OnEnable()
        {
            EditorHelper.GetWindow<GameHelperWindow>(this, true, "GameHelper");
            this.Init();
        }

        void OnGUI()
        {
            EditorHelper.ShowDefaultWindowContextMenuHandling();
            EditorHelper.RecordObjectWindow(this);

            _dragLineSeparator.BeginLayout(true);
            {
                DrawLeftWindow();
            }
            _dragLineSeparator.EndLayout();

            _dragLineSeparator.BeginLayout();
            {
                DrawRightWindow();
            }
            _dragLineSeparator.EndLayout();

            DrawSearchFiled();
        }

        void OnDestroy()
        {
            Undo.undoRedoPerformed -= OnUndoRedoPerformedCallBack;
        }

        private void Init()
        {
            _configData = shaco.GameHelper.gameConfig;
            LoadSettings();
            _dragLineSeparator.SetSplitWindow(this, 1f, 0.0f);

            //窗口初始化的时候重置一次弹出类型记录，以免增改删了类型后不能及时反应出来
            GUILayoutHelper.ResetPopTypeField();

            //监听undo事件
            Undo.undoRedoPerformed -= OnUndoRedoPerformedCallBack;
            Undo.undoRedoPerformed += OnUndoRedoPerformedCallBack;

            AnalyseGameHelperInterfaces();
        }

        //收集当前GameHelper中所有带GameInterfaceTypeAtrribute属性
        private void AnalyseGameHelperInterfaces()
        {
            _interfacesInfoList.Clear();
            _interfacesInfoBase.Clear();

            var instanceType = typeof(shaco.Base.IGameInstance);
            var targetAttributeType = typeof(shaco.Base.GameInterfaceTypeAtrribute);
            var interfacePropertiesFind = new List<KeyValuePair<System.Reflection.PropertyInfo, shaco.Base.GameInterfaceTypeAtrribute>>();
            var typeGameHelper = _currentGameHelper.GetType();
            var bindingFlags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static;
            var properties = typeGameHelper.GetProperties(bindingFlags).ToList();

            //依次获取父类信息，直到最上层父类
            typeGameHelper = typeGameHelper.BaseType;
            if (typeGameHelper.IsInherited(typeof(shaco.Base.IGameHelper)))
            {
                while (typeGameHelper != typeof(shaco.Base.IGameHelper))
                {
                    properties.AddRange(typeGameHelper.GetProperties(bindingFlags));
                    typeGameHelper = typeGameHelper.BaseType;
                }
                properties.AddRange(typeGameHelper.GetProperties(bindingFlags));
            }

            foreach (var iter in properties)
            {
                if (iter.PropertyType.IsInherited<ICollection>())
                {
                    System.Type typeTmp = iter.PropertyType;
                    var genericParams = typeTmp.GetGenericArguments();
                    bool isInheritedFromGameHelperType = false;

                    foreach (var iter2 in genericParams)
                    {
                        if (iter2.IsInherited(instanceType))
                        {
                            isInheritedFromGameHelperType = true;
                            break;
                        }
                    }

                    if (!isInheritedFromGameHelperType)
                        continue;
                }
                else
                {
                    //这里不用判断是否类型继承自IGameHelper了，因为可能属性类型要额外支持bool或者其他自定义类型
                    // if (!iter.PropertyType.IsInherited(instanceType))
                    //     continue;
                }

                var attributesFind = iter.GetCustomAttributes(false);
                if (attributesFind.IsNullOrEmpty())
                    continue;

                var attributeFind = attributesFind.Find(v => targetAttributeType.Equals(targetAttributeType)) as shaco.Base.GameInterfaceTypeAtrribute;
                if (null == attributeFind)
                    continue;

                var newKeyValue = new KeyValuePair<System.Reflection.PropertyInfo, shaco.Base.GameInterfaceTypeAtrribute>(iter, attributeFind);
                interfacePropertiesFind.Add(newKeyValue);
            }

            foreach (var iter in interfacePropertiesFind)
            {
                var propertyTmp = iter.Key;
                var attributeTmp = iter.Value;
                var interfaceInfo = new InterfaceInfo();
                var valueTmp = propertyTmp.GetValue(_currentGameHelper);

                interfaceInfo.interfaceTarget = valueTmp;
                interfaceInfo.propertyInfo = propertyTmp;
                interfaceInfo.header = attributeTmp.header;
                interfaceInfo.ignoreTypes = attributeTmp.ignoreTypes;
                interfaceInfo.isCustomDrawerOrBaseType = attributeTmp.isCustomDrawerOrBaseType;
                interfaceInfo.defaultListNewValueType = attributeTmp.defaultListNewValueType;

                if (propertyTmp.PropertyType.IsInherited<ICollection>())
                {
                    var genericParams = propertyTmp.PropertyType.GetGenericArguments();
                    foreach (var iter2 in genericParams)
                    {
                        if (iter2.IsInherited(instanceType))
                        {
                            interfaceInfo.genericListValueType = iter2;
                            break;
                        }
                    }

                    if (null == interfaceInfo.genericListValueType)
                    {
                        Debug.LogError("GameHelperWindow AnalyseGameHelperInterfaces error: not found list value type, list=" + propertyTmp.Name);
                        continue;
                    }
                    interfaceInfo.drawInterfaceCallBack = DrawHelperInterfacesBase;
                    List<InterfaceInfo> findGropuInfo = null;
                    if (!_interfacesInfoList.TryGetValue(attributeTmp.groupName, out findGropuInfo))
                    {
                        findGropuInfo = new List<InterfaceInfo>();
                        _interfacesInfoList.Add(attributeTmp.groupName, findGropuInfo);
                    }
                    findGropuInfo.Add(interfaceInfo);
                }
                else
                {
                    interfaceInfo.drawInterfaceCallBack = DrawHelperInterfaceBase;

                    List<InterfaceInfo> findGropuInfo = null;
                    if (!_interfacesInfoBase.TryGetValue(attributeTmp.groupName, out findGropuInfo))
                    {
                        findGropuInfo = new List<InterfaceInfo>();
                        _interfacesInfoBase.Add(attributeTmp.groupName, findGropuInfo);
                    }

                    findGropuInfo.Add(interfaceInfo);
                }
            }
        }

        private void OnUndoRedoPerformedCallBack()
        {
        }

        private void LoadSettings()
        {
            var typeNameTmp = _configData.ReadString(typeof(shaco.Base.IGameHelper).ToTypeString(), typeof(shaco.GameHelper).ToTypeString());
            _currentGameHelper = (shaco.Base.IGameHelper)shaco.Base.Utility.Instantiate(typeNameTmp);
            if (null == _currentGameHelper)
                _currentGameHelper = new shaco.GameHelper();
            _isShacoFrameworkGameHelper = _currentGameHelper.GetType().IsInherited(typeof(shaco.GameHelper));
        }

        private void SaveSettings()
        {
            _configData.WriteString(typeof(shaco.Base.IGameHelper).ToTypeString(), _currentGameHelper.ToTypeString());
            _configData.CheckSaveModifyData();
        }

        private void DrawSearchFiled()
        {
            GUILayout.BeginHorizontal();
            {
                GUILayout.Space(Screen.width / 3 * 2);
                var newSearchName = GUILayoutHelper.SearchField(_searchName);
                if (newSearchName != _searchName)
                {
                    _searchName = newSearchName;
                    _searchNameLower = _searchName.ToLower();
                }
            }
            GUILayout.EndHorizontal();
        }

        private void DrawFrameworkVersion()
        {
            EditorGUILayout.LabelField("Framework Version", shaco.Base.GlobalParams.SHACO_GAME_FRAMEWORK_VERSION);
        }

        private void DrawCurrentGameHelper()
        {
            var newGameHelper = GUILayoutHelper.PopupTypeField(GUI_CONTENT_GAME_HELPER_SETTING, _currentGameHelper, null);
            if (newGameHelper != _currentGameHelper)
            {
                _currentGameHelper = newGameHelper;
                _isShacoFrameworkGameHelper = _currentGameHelper.GetType().IsInherited(typeof(shaco.GameHelper));
                _currentGameHelper.InitFramework();
                SaveSettings();
                AnalyseGameHelperInterfaces();
            }
        }

        private void DrawLeftWindow()
        {
            _leftWindowScrollPos = GUILayout.BeginScrollView(_leftWindowScrollPos);
            {
                DrawFrameworkVersion();
                DrawCurrentGameHelper();

                if (_isShacoFrameworkGameHelper)
                {
                    GUILayout.BeginHorizontal();
                    {
                        EditorGUI.BeginDisabledGroup(true);
                        {
                            EditorGUILayout.EnumPopup(GUI_CONTENT_EXCEL_LOAD_FILE_TYPE, shaco.GameHelper.excelSetting.runtimeLoadFileType);
                        }
                        EditorGUI.EndDisabledGroup();
                    }
                    GUILayout.EndHorizontal();
                }

                DrawHelperInterfaces();
            }
            GUILayout.EndScrollView();
        }

        private void DrawRightWindow()
        {
            // _rightWindowScrollPos = GUILayout.BeginScrollView(_rightWindowScrollPos);
            // {
            //     GUILayout.Space(16);
            //     GUILayout.Label("Runtime Instance Componnets");
            //     shaco.GameEntry.Foreach((type, value) =>
            //     {
            //         var typeStringTmp = type.ToTypeString();
            //         if (!string.IsNullOrEmpty(_searchNameLower) && !typeStringTmp.ToLower().Contains(_searchNameLower))
            //         {
            //             return;
            //         }

            //         GUILayout.BeginHorizontal();
            //         {
            //             GUILayout.Label(typeStringTmp, GUILayout.Width(Screen.width * 0.1f));
            //             GUILayout.TextField(value.ToTypeString());
            //         }
            //         GUILayout.EndHorizontal();
            //     });
            // }
            // GUILayout.EndScrollView();
        }

        private void DrawHelperInterfaces()
        {
            foreach (var iter in _interfacesInfoList)
            {
                GUILayout.BeginHorizontal();
                {
                    for (int i = 0; i < iter.Value.Count; ++i)
                        DrawHelperInterfaces(iter.Value[i]);
                }
                GUILayout.EndHorizontal();
                GUILayoutHelper.DrawSeparatorLine();
            }

            foreach (var iter in _interfacesInfoBase)
            {
                bool foundInterface = true;
                if (!string.IsNullOrEmpty(_searchNameLower))
                {
                    foundInterface = iter.Value.Any(v => v.propertyInfo.PropertyType.ToTypeString().ToLower().Contains(_searchNameLower));
                }

                if (foundInterface)
                {
                    var titleTmp = iter.Key.ToString();
                    if (GUILayoutHelper.DrawHeader(titleTmp, "GameHelperWindow_" + titleTmp))
                    {
                        for (int i = 0; i < iter.Value.Count; ++i)
                        {
                            DrawHelperInterface(iter.Value[i]);
                        }
                    }
                }
            }
        }

        private void DrawHelperInterface(InterfaceInfo info)
        {
            if (info.isCustomDrawerOrBaseType)
            {
                GUI.changed = false;
                info.interfaceTarget = GUILayoutHelper.DrawValue(info.header, info.interfaceTarget, info.propertyInfo.PropertyType, info);
                if (GUI.changed)
                {
                    info.propertyInfo.SetValue(_currentGameHelper, info.interfaceTarget);
                }
            }
            else
            {
                DrawHelperInterfaceBase(info);
            }
        }

        private void DrawHelperInterfaceBase(InterfaceInfo info)
        {
            if (!string.IsNullOrEmpty(_searchNameLower) && !info.propertyInfo.PropertyType.ToTypeString().ToLower().Contains(_searchNameLower))
            {
                return;
            }

            GUILayout.BeginHorizontal();
            {
                GUILayoutHelper.PopupTypeField(info.header, info.interfaceTarget, info.propertyInfo.PropertyType, (type) =>
                {
                    var instantiatedValue = type.Instantiate();

                    //修改类型
                    info.interfaceTarget = instantiatedValue;
                    info.propertyInfo.SetValue(info.interfaceTarget, instantiatedValue);

                    //保存类型
                    _configData.WriteString(info.propertyInfo.PropertyType.ToTypeString(), instantiatedValue.ToTypeString());
                    return instantiatedValue;
                }, info.ignoreTypes);
            }
            GUILayout.EndHorizontal();
        }

        private void DrawHelperInterfaces(InterfaceInfo info)
        {
            if (info.isCustomDrawerOrBaseType)
            {
                GUILayoutHelper.DrawValue(info.header, info.interfaceTarget, info.propertyInfo.PropertyType, info);
            }
            else
            {
                DrawHelperInterfacesBase(info);
            }
        }

        private void DrawHelperInterfacesBase(InterfaceInfo info)
        {
            if (!string.IsNullOrEmpty(_searchNameLower) && !info.propertyInfo.PropertyType.ToTypeString().ToLower().Contains(_searchNameLower))
            {
                return;
            }

            GUILayout.BeginVertical();
            {
                GUI.changed = false;
                var collectionTmp = (IList)info.interfaceTarget;
                GUILayoutHelper.DrawListBase<object>(collectionTmp, info.genericListValueType, info.header, null, (callbackNew) =>
                {
                    if (null == info.defaultListNewValueType)
                    {
                        Debug.LogError("GameHelperWindow DrawHelperInterfaces error: default list new value type is null, header=" + info.header);
                        return;
                    }
                    var defaultValue = info.defaultListNewValueType.Instantiate();
                    if (null == defaultValue)
                    {
                        Debug.LogError("GameHelperWindow DrawHelperInterfaces error: incorrect default value type=" + info.defaultListNewValueType.ToTypeString() + " header=" + info.header);
                        return;
                    }

                    callbackNew(defaultValue);
                }, null, null, (index, value, callbackChanged) =>
                {
                    var newValue = GUILayoutHelper.PopupTypeField("Item " + index, value, info.genericListValueType, null, info.ignoreTypes);
                    if (GUI.changed)
                    {
                        callbackChanged(newValue);
                    }
                    return true;
                }, null);

                if (GUI.changed)
                {
                    var listTmp = new List<string>();
                    for (int i = 0; i < collectionTmp.Count; ++i)
                        listTmp.Add(collectionTmp[i].ToString());
                    _configData.WriteList(info.genericListValueType.ToTypeString(), listTmp);
                }
            }
            GUILayout.EndVertical();
        }
    }
}